//
//    Gen Vertices
//


-- VS


layout(location=0) in int x6y6z6_e4e4e4;

out block {
  vec3 v1, n1;
  vec3 v2, n2;
  vec3 v3, n3;
} Out;


uniform sampler3D uDensityVolume;
uniform float uInvChunkDim;
uniform float uChunkSize;
uniform vec3 uChunkPositionWS;


const vec3 uEdgeStart[12] = {{0, 0, 0}, {0, 1, 0}, {1, 0, 0}, {0, 0, 0}, 
                             {0, 0, 1}, {0, 1, 1}, {1, 0, 1}, {0, 0, 1}, 
                             {0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0}};

const vec3 uEdgeEnd[12] = {{0, 1, 0}, {1, 1, 0}, {1, 1, 0}, {1, 0, 0}, 
                           {0, 1, 1}, {1, 1, 1}, {1, 1, 1}, {1, 0, 1}, 
                           {0, 0, 1}, {0, 1, 1}, {1, 1, 1}, {1, 0, 1}};

const vec3 uEdgeDir[12] = {{0, 1, 0}, {1, 0, 0}, {0, 1, 0}, {1, 0, 0}, 
                           {0, 1, 0}, {1, 0, 0}, {0, 1, 0}, {1, 0, 0}, 
                           {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1}};


struct Vertex
{
  vec3 position;
  vec3 normal;
};

Vertex placeVertexOnEdge( vec3 wsCoord, vec3 texCoord, int edgeNum)
{
  Vertex v;
  
#define uInvVoxelDimMinusOne  (1.0f/32.0f)   //
#define uVoxelSize            (1.0f/32.0f)   //
  
  // Compute the position
  float p1 = texture( uDensityVolume, texCoord + uInvVoxelDimMinusOne*uEdgeStart[edgeNum]).r;
  float p2 = texture( uDensityVolume, texCoord + uInvVoxelDimMinusOne*uEdgeEnd[edgeNum]  ).r;
  float t = p1 / (p1 - p2);
  t = clamp( t, 0.0f, 1.0f);
  
  vec3 pos_within_cell = uEdgeStart[edgeNum] + t * uEdgeDir[edgeNum];  
  v.position = wsCoord + uVoxelSize * pos_within_cell;
  
  // Compute the normal
  vec3 uvw = texCoord + 1*pos_within_cell/32.0f;//uInvChunkDim * pos_within_cell;
  const vec2 texelSize = vec2( uInvChunkDim, 0.0f);//

  vec3 gradient;
  gradient.x = texture( uDensityVolume, uvw - texelSize.xyy).r - 
               texture( uDensityVolume, uvw + texelSize.xyy).r;
  gradient.y = texture( uDensityVolume, uvw - texelSize.yxy).r - 
               texture( uDensityVolume, uvw + texelSize.yxy).r;
  gradient.z = texture( uDensityVolume, uvw - texelSize.yyx).r - 
               texture( uDensityVolume, uvw + texelSize.yyx).r;
  v.normal = normalize(gradient);
  
  //v.normal = normalize(v.position);
  
  return v;
}

void main()
{

  int coords = x6y6z6_e4e4e4 & 0x7ffff;
  int edges  = (x6y6z6_e4e4e4 >> 18) & 0xfff;  
    
  ivec3 voxelCoord;
  voxelCoord.x = (coords >>  0) & 0x3F;
  voxelCoord.y = (coords >>  6) & 0x3F;
  voxelCoord.z = (coords >> 12) & 0x3F;

  ivec3 edgeNum;
  edgeNum.x = (edges >> 0) & 0x0F;
  edgeNum.y = (edges >> 4) & 0x0F;
  edgeNum.z = (edges >> 8) & 0x0F;
    

  vec3 uvw = uInvChunkDim * voxelCoord;  //(1 + 2*voxelCoord)/(66.);
  vec3 wsCoord = uChunkPositionWS + uChunkSize * uvw;


  Vertex v;
    
  v = placeVertexOnEdge( wsCoord, uvw, edgeNum.x);
  Out.v1 = v.position;
  Out.n1 = v.normal;
  
  v = placeVertexOnEdge( wsCoord, uvw, edgeNum.y);
  Out.v2 = v.position;
  Out.n2 = v.normal;
  
  v = placeVertexOnEdge( wsCoord, uvw, edgeNum.z);
  Out.v3 = v.position;
  Out.n3 = v.normal;
  
}

--

//------------------------------------------------------------------------------

-- GS

layout(points) in;
layout(points, max_vertices = 3) out;

in block {
  vec3 v1, n1;
  vec3 v2, n2;
  vec3 v3, n3;
} In[];

out vec3 outPositionWS;
out vec3 outNormalWS;


void main()
{
  outPositionWS = In[0].v1;
  outNormalWS   = In[0].n1;
  EmitVertex();
  
  outPositionWS = In[0].v2;
  outNormalWS   = In[0].n2;
  EmitVertex();
  
  outPositionWS = In[0].v3;
  outNormalWS   = In[0].n3;
  EmitVertex();
  
  EndPrimitive();
}

